/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package segmented_control.widget.custom.ohos.com.segmentedcontrol;

import ohos.agp.components.Component;
import ohos.agp.components.Component.TouchEventListener;
import ohos.agp.components.VelocityDetector;
import ohos.multimodalinput.event.TouchEvent;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;

/**
 * 用于修复ohos{@link TouchEvent}内的一些数据错误，并矫正由此产生的一些计算错误，如：使用{@link VelocityDetector}计算速度等。
 * 此类适用于单手指移动的情况，暂未适配多手指，也不适用于{@link TouchEvent#getPointerScreenPosition(int)}。
 * 在{@link TouchEventListener#onTouchEvent(Component, TouchEvent)}内调用{@link #correctY(TouchEvent)}即可。
 * 注意：请勿重复或在滑动过程中构造此对象，构造函数中使用了反射，可能会造成性能问题。
 */
public class TouchEventCompact {
    /**
     * originActionCancel
     */
    private Field originActionCancel;
    /**
     * offsetLocationMethod
     */
    private Method offsetLocationMethod;
    /**
     * setActionMethod
     */
    private Method setActionMethod;
    /**
     * getPointerIdBitsMethod
     */
    private Method getPointerIdBitsMethod;
    /**
     * ohosField
     */
    private Field ohosField;
    /**
     * downY
     */
    private float downY;
    /**
     * corrected
     */
    private boolean corrected = false;
    /**
     * offsetY
     */
    private float offsetY = 0;

    /**
     * 不要反复构造对象
     */
    public TouchEventCompact() {
        try {
            Class<?> origin = Class.forName("android.view.MotionEvent");
            for (Method declaredMethod : origin.getDeclaredMethods()) {
                if ("offsetLocation".equals(declaredMethod.getName())) {
                    declaredMethod.setAccessible(true);
                    offsetLocationMethod = declaredMethod;
                }

                if ("setAction".equals(declaredMethod.getName())) {
                    declaredMethod.setAccessible(true);
                    setActionMethod = declaredMethod;
                }

                if ("getPointerIdBits".equals(declaredMethod.getName())) {
                    declaredMethod.setAccessible(true);
                    getPointerIdBitsMethod = declaredMethod;
                }
            }

            originActionCancel = origin.getDeclaredField("ACTION_CANCEL");

            Class<?> impl = Class.forName("ohos.multimodalinput.eventimpl.TouchEventImpl");
            ohosField = impl.getDeclaredField("motionEvent");
            ohosField.setAccessible(true);

        } catch (ClassNotFoundException | NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    /**
     * 修正{@link TouchEvent}的y轴偏移
     *
     * @param ev 待修正的TouchEvent
     */
    public void correctY(TouchEvent ev) {
        float y1 = getY(ev);
        switch (ev.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                recycle();
                downY = y1;
                break;

            case TouchEvent.POINT_MOVE:
                if (!corrected) {
                    offsetY = BigDecimal.valueOf(downY).subtract(BigDecimal.valueOf(y1)).floatValue();
                    corrected = true;
                }
                motionEventOffsetLocation(ev, 0, (float) offsetY);
                break;

            case TouchEvent.PRIMARY_POINT_UP:
            case TouchEvent.CANCEL:
                recycle();
                break;
        }
    }

    /**
     * 调整MotionEvent的位置
     *
     * @param ev 包含MotionEvent的TouchEvent
     * @param deltaX Amount to add to the current X coordinate of the event.
     * @param deltaY Amount to add to the current Y coordinate of the event.
     */
    public void motionEventOffsetLocation(TouchEvent ev, float deltaX, float deltaY) {
        try {
            Object mv = ohosField.get(ev);
            offsetLocationMethod.invoke(mv, 0, offsetY);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置MotionEvent的action
     *
     * @param ev 包含MotionEvent的TouchEvent
     * @param action 需要设置的action
     */
    public void motionEventSetAction(TouchEvent ev, int action) {
        try {
            Object mv = ohosField.get(ev);
            setActionMethod.invoke(mv, action);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取MotionEvent内的ACTION_CANCEL的值
     *
     * @param ev 包含MotionEvent的TouchEvent
     * @return ACTION_CANCEL的值
     */
    public int getOriginActionCancel(TouchEvent ev) {
        try {
            Object mv = ohosField.get(ev);
            return originActionCancel.getInt(mv);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return 0;
    }

    /**
     * 获取OriginPointer
     *
     * @param ev ev
     * @return OriginPointer
     */
    public int getOriginPointerIdBits(TouchEvent ev) {
        try {
            Object mv = ohosField.get(ev);
            return (int) getPointerIdBitsMethod.invoke(mv);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        return 0;
    }

    /**
     * 重置相关数据
     */
    public void recycle() {
        downY = 0;
        corrected = false;
        offsetY = 0;
    }

    private float getY(TouchEvent ev) {
        return ev.getPointerPosition(ev.getIndex()).getY();
    }
}
